home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / hash / shsdrvr.c < prev    next >
Encoding:
Text File  |  1994-03-25  |  23.7 KB  |  910 lines  |  [TEXT/R*ch]

  1. /* %W% %G% %U% */
  2. /*
  3.  * shsdrvr - shs driver code
  4.  *
  5.  * This file was written by:
  6.  *
  7.  *     Landon Curt Noll  (chongo@toad.com)    chongo <was here> /\../\
  8.  *
  9.  * This code has been placed in the public domain.  Please do not
  10.  * copyright this code.
  11.  *
  12.  * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH  REGARD  TO
  13.  * THIS  SOFTWARE,  INCLUDING  ALL IMPLIED WARRANTIES OF MER-
  14.  * CHANTABILITY AND FITNESS.  IN NO EVENT SHALL  LANDON  CURT
  15.  * NOLL  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  16.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM  LOSS  OF
  17.  * USE,  DATA  OR  PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  18.  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR  IN
  19.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  20.  *
  21.  ***
  22.  *
  23.  * NOTE: The version information below refers to all shs code, not
  24.  *     just this file.  In particular, this file was created by
  25.  *     Landon Curt Noll.
  26.  *
  27.  * Version 1.1: 02 Sep 1992?            original authors
  28.  *     This code is based on code by Peter C. Gutmann.  Much thanks goes
  29.  *     to Peter C. Gutman (pgut1@cs.aukuni.ac.nz) , Shawn A. Clifford
  30.  *     (sac@eng.ufl.edu), Pat Myrto (pat@rwing.uucp) and others who wrote
  31.  *     and/or worked on the original code.
  32.  *
  33.  * Version 2.1:    31 Dec 1993        Landon Curt Noll   (chongo@toad.com)
  34.  *     Reformatted, performance improvements and bug fixes
  35.  *
  36.  * Version 2.2:    02 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  37.  *     fixed -p usage
  38.  *     better error messages
  39.  *     added -c help
  40.  *     added -c 0    (concatenation)
  41.  *     reordered -i stat buffer pre-pending
  42.  *
  43.  * Version 2.3:    03 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  44.  *     added -c 1    (side by side)
  45.  *     added -c 2    (even force to be odd)
  46.  *     added -c x    (shs dual test suite)
  47.  *     changed -c help to be -c h
  48.  *     changed -c operand to type[,opt[,...]]
  49.  *     prefix & string ABI now can take arbitrary binary data
  50.  *     fixed memory leak
  51.  *     fixed even/odd byte split bug
  52.  *     added -P file
  53.  *     added -q
  54.  *     added UNROLL_LOOPS to control shs.c loop unrolling
  55.  *     major performance improvements
  56.  *
  57.  * Version 2.4: 05 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  58.  *     renamed calc mode to dual mode
  59.  *     removed all -c code
  60.  *     added -d        (dual digests, space separated)
  61.  *     rewrote most of the file, string and stream logic using shsdual code
  62.  *
  63.  * Version 2.5: 08 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  64.  *     added (new) -c    (print 0x in front of digests)
  65.  *     removed st_blksize and st_blocks from -i preprocessing data
  66.  *     only print .0 suffix if -i and digesting a file
  67.  *     non-zero edit codes are now unique
  68.  *     changed the dual test suite (shorter, added non alpha numeric chars)
  69.  *     -i requires filenames
  70.  *     fixed @(#) what string code
  71.  *     boolean logic simplication by Rich Schroeppel (rcs@cs.arizona.edu)
  72.  *     on the fly in a circular buffer by Colin Plumb (colin@nyx10.cs.du.edu)
  73.  *
  74.  * Version 2.6: 11 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  75.  *     Merged the shs and md5 Makefiles to build both in the same directory
  76.  *     alignment and byte order performance improvements
  77.  *     eliminate wateful memory copies
  78.  *     shs transform contains no function calls
  79.  *     beta release
  80.  *
  81.  * Version 2.7: 13 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  82.  *     code cleanup
  83.  *     chunk is now 64 bytes, block is determined by blocking factor
  84.  *     magic 64 and 64 related values defined in terms of #defines
  85.  *     added blocking code (-b block_len)
  86.  *     added xor feedback code (-f)
  87.  *     added xor feedback and block options to performance test
  88.  *     performance improvements
  89.  *
  90.  * Version 2.8: 16 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  91.  *     code cleanup
  92.  *     performance improvements
  93.  *     removed blocking and feedback code
  94.  *     count bytes in driver, convert to 64 bit count in final transform
  95.  *     added debug mode
  96.  *     handle read errors and EOF better
  97.  *     prefix strings not multiple of 64 bytes in length do not slow down hash
  98.  *     renumbered exit codes
  99.  *     fixed dual digest split bug
  100.  *     byte sex swapping is now controlled thru the SHS_TRANSFORM macro
  101.  *     shsTransform() is now called via the SHS_TRANSFORM macro
  102.  *
  103.  * Version 2.9: 12 Feb 1994        Landon Curt Noll   (chongo@toad.com)
  104.  *     prep for beta release
  105.  *     removed all feedback code
  106.  *
  107.  * Version 2.10: 25 Mar 1994        Landon Curt Noll   (chongo@toad.com)
  108.  *     must_align catchs signal to detect misaligned access
  109.  */
  110.  
  111. #include <stdio.h>
  112. #include <stdlib.h>
  113. #include <string.h>
  114. #include <time.h>
  115. #include <sys/time.h>
  116. #include <sys/resource.h>
  117. #include "shs.h"
  118.  
  119. /* size of test in megabytes */
  120. #define TEST_MEG 16
  121.  
  122. /* number of chunks to process */
  123. #define TEST_CHUNKS (TEST_MEG*1024*1024/READSIZE)
  124.  
  125. /* SHS test suite strings */
  126. #define ENTRY(str) {(BYTE *)str, NULL, sizeof(str)-1}
  127. struct shs_test {
  128.     BYTE *ro_data;    /* read only string data or NULL to test */
  129.     BYTE *data;        /* data or NULL to test */
  130.     int len;        /* length of data */
  131. } shs_test_data[] = {
  132.     ENTRY(""),
  133.     ENTRY("a"),
  134.     ENTRY("abc"),
  135.     ENTRY("message digest"),
  136.     ENTRY("abcdefghijklmnopqrstuvwxyz"),
  137.     ENTRY("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
  138.     ENTRY("12345678901234567890123456789012345678901234567890123456789012345678901234567890")
  139. };
  140. #define MAX_SHS_TEST_DATA (sizeof(shs_test_data)/sizeof(shs_test_data[0]))
  141.  
  142. /* shs test filenames */
  143. char *shs_test_file[] = {
  144.     "file1",
  145.     "file2",
  146. };
  147. #define MAX_SHS_TEST_FILE (sizeof(shs_test_file)/sizeof(shs_test_file[0]))
  148.  
  149. /* where the test files are located by default */
  150. #if !defined(TLIB)
  151. #define TLIB "."
  152. #endif
  153.  
  154. /* Prototypes of the static functions */
  155. static void shsStream P((BYTE*, UINT, FILE*, SHS_INFO*));
  156. static void shsFile P((BYTE*, UINT, char*, SHS_INFO*));
  157. static void shsOutput P((char*, int, SHS_INFO*));
  158. static int shsPreFileRead P((char*, BYTE**));
  159. static void shsTestSuite P((void));
  160. static void shsHelp P((void));
  161. void main P((int, char**));
  162.  
  163. /* global variables */
  164. char *program;            /* our name */
  165. static int c_flag = 0;        /* 1 => print C style digest with leading 0x */
  166. int debug = 0;            /* 1 => add debug */
  167. int i_flag = 0;            /* 1 => process inode & filename */
  168. int q_flag = 0;            /* 1 => print the digest only */
  169. int dot_zero = 0;        /* 1 => print .0 after the digest */
  170. ULONG zero[SHS_CHUNKWORDS];    /* block of zeros */
  171.  
  172.  
  173. /*
  174.  * shsStream - digest a open file stream
  175.  */
  176. static void
  177. shsStream(pre_str, pre_len, stream, dig)
  178.     BYTE *pre_str;        /* data prefix or NULL */
  179.     UINT pre_len;        /* length of pre_str */
  180.     FILE *stream;        /* the stream to process */
  181.     SHS_INFO *dig;        /* current digest */
  182. {
  183.     ULONG data[READWORDS];    /* our read buffer */
  184.     int bytes;            /* bytes last read */
  185.     int ret;            /* partial fread return value */
  186.  
  187.     /*
  188.      * pre-process prefix if needed
  189.      */
  190.     if (pre_str != NULL) {
  191.     shsUpdate(dig, pre_str, pre_len);
  192.     COUNT(dig, pre_len);
  193.     }
  194.  
  195.     /*
  196.      * if we have a partial chunk, try to read until we have a full chunk
  197.      */
  198.     clearerr(stream);
  199.     if (dig->datalen > 0) {
  200.  
  201.         /* determine what we have so far */
  202.         bytes = dig->datalen;
  203.  
  204.     /* try to read what we need to fill the chunk */
  205.     while (bytes < SHS_CHUNKSIZE) {
  206.  
  207.         /* try to read what we need */
  208.         ret = fread((char*)data+bytes, 1, SHS_CHUNKSIZE-bytes, stream);
  209.  
  210.         /* carefully examine the result */
  211.         if (ret < 0 || ferror(stream)) {
  212.         /* error processing */
  213.         fprintf(stderr, "%s: ", program);
  214.         perror("read #3 error");
  215.         exit(1);
  216.         } else if (ret == 0 || feof(stream)) {
  217.         /* EOF processing */
  218.         COUNT(dig, SHS_CHUNKSIZE-dig->datalen);
  219.         shsUpdate(dig, (BYTE *)data+dig->datalen, 
  220.           SHS_CHUNKSIZE-dig->datalen);
  221.         return;
  222.         }
  223.  
  224.         /* note that we have more bytes */
  225.         bytes += ret;
  226.         }
  227.         COUNT(dig, SHS_CHUNKSIZE-dig->datalen);
  228.         shsUpdate(dig, (BYTE *)data+dig->datalen, SHS_CHUNKSIZE-dig->datalen);
  229.     }
  230.  
  231.     /*
  232.      * process the contents of the file
  233.      */
  234.     while ((bytes = fread((char *)data, 1, READSIZE, stream)) > 0) {
  235.  
  236.     /*
  237.      * if we got a partial read, try to read up to a full chunk
  238.      */
  239.     while (bytes < READSIZE) {
  240.  
  241.         /* try to read more */
  242.         ret = fread((char *)data+bytes, 1, READSIZE-bytes, stream);
  243.  
  244.         /* carefully examine the result */
  245.         if (ret < 0 || ferror(stream)) {
  246.             /* error processing */
  247.             fprintf(stderr, "%s: ", program);
  248.             perror("read #1 error");
  249.             exit(2);
  250.         } else if (ret == 0 || feof(stream)) {
  251.             /* EOF processing */
  252.             shsUpdate(dig, (BYTE *)data, bytes);
  253.             COUNT(dig, bytes);
  254.             return;
  255.         }
  256.  
  257.         /* note that we have more bytes */
  258.         bytes += ret;
  259.     }
  260.  
  261.     /*
  262.      * digest the read
  263.      */
  264.     shsfullUpdate(dig, (BYTE *)data, bytes);
  265.     COUNT(dig, bytes);
  266.     }
  267.  
  268.     /*
  269.      * watch for errors
  270.      */
  271.     if (bytes < 0 || ferror(stream)) {
  272.     /* error processing */
  273.     fprintf(stderr, "%s: ", program);
  274.     perror("read #2 error");
  275.     exit(3);
  276.     }
  277.     return;
  278. }
  279.  
  280.  
  281. /*
  282.  * shsFile - digest a file
  283.  */
  284. static void
  285. shsFile(pre_str, pre_len, filename, dig)
  286.     BYTE *pre_str;        /* string prefix or NULL */
  287.     UINT pre_len;        /* length of pre_str */
  288.     char *filename;        /* the filename to process */
  289.     SHS_INFO *dig;        /* current digest */
  290. {
  291.     FILE *inFile;        /* the open file stream */
  292.     struct stat buf;        /* stat or lstat of file */
  293.     struct hashstat hashbuf;    /* stat data to digest */
  294.     struct hashstat hashlbuf;    /* lstat data to digest */
  295.     ULONG filename_len;        /* length of the filename */
  296.  
  297.     /*
  298.      * open the file
  299.      */
  300.     inFile = fopen(filename, "rb");
  301.     if (inFile == NULL) {
  302.     fprintf(stderr, "%s: cannot open %s: ", program, filename);
  303.     perror("");
  304.     return;
  305.     }
  306.  
  307.     /*
  308.      * pre-process prefix if needed
  309.      */
  310.     if (pre_str == NULL) {
  311.     if (i_flag) {
  312.         filename_len = strlen(filename);
  313.         shsUpdate(dig, (BYTE *)filename, filename_len);
  314.         COUNT(dig, filename_len);
  315. #if defined(DEBUG)
  316.         if (debug) {
  317.         fprintf(stderr, "DEBUG: filename_len:%d count:%d\n",
  318.             filename_len, dig->countLo);
  319.         }
  320. #endif
  321.     }
  322.     } else {
  323.     if (i_flag) {
  324.         shsUpdate(dig, pre_str, pre_len);
  325.         filename_len = strlen(filename);
  326.         shsUpdate(dig, (BYTE *)filename, filename_len);
  327.         COUNT(dig, filename_len+pre_len);
  328. #if defined(DEBUG)
  329.         if (debug) {
  330.         fprintf(stderr, "DEBUG: pre_len:%d filename_len:%d count:%d\n",
  331.             pre_len, filename_len, dig->countLo);
  332.         }
  333. #endif
  334.     } else {
  335.         shsUpdate(dig, pre_str, pre_len);
  336.         COUNT(dig, pre_len);
  337.     }
  338.     }
  339.  
  340.     /*
  341.      * digest file stat and lstat
  342.      */
  343.     if (i_flag) {
  344.     if (fstat(fileno(inFile), &buf) < 0) {
  345.         printf("%s can't be stated.\n", filename);
  346.         return;
  347.     }
  348.     hashbuf.st_dev = buf.st_dev;
  349.     hashbuf.st_ino = buf.st_ino;
  350.     hashbuf.st_mode = buf.st_mode;
  351.     hashbuf.st_nlink = buf.st_nlink;
  352.     hashbuf.st_uid = buf.st_uid;
  353.     hashbuf.st_gid = buf.st_gid;
  354.     hashbuf.st_size = buf.st_size;
  355.     hashbuf.st_mtime = buf.st_mtime;
  356.     hashbuf.st_ctime = buf.st_ctime;
  357. #if defined(DEBUG)
  358.     if (debug) {
  359.         fprintf(stderr, 
  360.           "DEBUG: dev:%d ino:%d mode:%o nlink:%d uid:%d gid:%d\n",
  361.           hashbuf.st_dev, hashbuf.st_ino, hashbuf.st_mode,
  362.           hashbuf.st_nlink, hashbuf.st_uid, hashbuf.st_gid);
  363.         fprintf(stderr, 
  364.           "DEBUG: size:%d mtime:%d ctime:%d\n",
  365.           hashbuf.st_size, hashbuf.st_mtime, hashbuf.st_ctime);
  366.     }
  367. #endif
  368.     shsUpdate(dig, (BYTE *)&hashbuf, sizeof(hashbuf));
  369.     if (lstat(filename, &buf) < 0) {
  370.         printf("%s can't be lstated.\n", filename);
  371.         return;
  372.     }
  373.     hashlbuf.st_dev = buf.st_dev;
  374.     hashlbuf.st_ino = buf.st_ino;
  375.     hashlbuf.st_mode = buf.st_mode;
  376.     hashlbuf.st_nlink = buf.st_nlink;
  377.     hashlbuf.st_uid = buf.st_uid;
  378.     hashlbuf.st_gid = buf.st_gid;
  379.     hashlbuf.st_size = buf.st_size;
  380.     hashlbuf.st_mtime = buf.st_mtime;
  381.     hashlbuf.st_ctime = buf.st_ctime;
  382. #if defined(DEBUG)
  383.     if (debug) {
  384.         fprintf(stderr, 
  385.           "DEBUG: ldev:%d lino:%d mode:%o lnlink:%d luid:%d lgid:%d\n",
  386.           hashlbuf.st_dev, hashlbuf.st_ino, hashlbuf.st_mode,
  387.           hashlbuf.st_nlink, hashlbuf.st_uid, hashlbuf.st_gid);
  388.         fprintf(stderr, 
  389.           "DEBUG: lsize:%d lmtime:%d lctime:%d\n",
  390.           hashlbuf.st_size, hashlbuf.st_mtime, hashlbuf.st_ctime);
  391.     }
  392. #endif
  393.     shsUpdate(dig, (BYTE *)&hashlbuf, sizeof(hashlbuf));
  394.  
  395.     /*
  396.      * pad with zeros to process file data faster
  397.      */
  398.     if (dig->datalen > 0) {
  399. #if defined(DEBUG)
  400.         if (debug) {
  401.         fprintf(stderr, 
  402.           "DEBUG: pad_len:%d\n", SHS_CHUNKSIZE - dig->datalen);
  403.         }
  404. #endif
  405.         COUNT(dig, sizeof(hashbuf) + sizeof(hashlbuf) + 
  406.               SHS_CHUNKSIZE - dig->datalen);
  407.         shsUpdate(dig, (BYTE *)zero, SHS_CHUNKSIZE - dig->datalen);
  408.     } else {
  409.         COUNT(dig, sizeof(hashbuf) + sizeof(hashlbuf));
  410.     }
  411. #if defined(DEBUG)
  412.     if (debug) {
  413.         fprintf(stderr, "DEBUG: datalen:%d count:%d\n", 
  414.           dig->datalen, dig->countLo);
  415.     }
  416. #endif
  417.     }
  418.  
  419.     /*
  420.      * process the data stream
  421.      */
  422.     shsStream(NULL, 0, inFile, dig);
  423.     fclose(inFile);
  424. }
  425.  
  426.  
  427. /*
  428.  * shsOutput - output the digest
  429.  */
  430. static void
  431. shsOutput(str, quot, dig)
  432.     char *str;        /* print string after digest, NULL => none */
  433.     int quot;        /* 1 => surround str with a double quotes */
  434.     SHS_INFO *dig;    /* current digest */
  435. {
  436.     /*
  437.      * finalize the digest
  438.      */
  439.     shsFinal(dig);
  440. #if defined(DEBUG)
  441.     if (debug) {
  442.     fprintf(stderr, 
  443.       "DEBUG: 64 bit count: 0x%08x%08x\n",
  444.       ((dig->countHi << 3) | (dig->countLo >> 29)), (dig->countLo << 3));
  445.     }
  446. #endif
  447.  
  448.     /*
  449.      * print the digest
  450.      */
  451.     shsPrint(dig);
  452.     if (str && !q_flag) {
  453.     if (quot) {
  454.         printf(" \"%s\"\n", str);
  455.     } else {
  456.         printf(" %s\n", str);
  457.     }
  458.     } else {
  459.     putchar('\n');
  460.     }
  461.     fflush(stdout);
  462. }
  463.  
  464.  
  465. /*
  466.  * shsPrint - print a digest in hex
  467.  *
  468.  * Prints message digest buffer in shsInfo as 40 hexadecimal digits. Order is
  469.  * from low-order byte to high-order byte of digest. Each byte is printed
  470.  * with high-order hexadecimal digit first.
  471.  *
  472.  * If -c, then print a leading "0x".  If -i, then print a trailing ".0".
  473.  */
  474. void
  475. shsPrint(shsInfo)
  476.     SHS_INFO *shsInfo;
  477. {
  478.     if (c_flag) {
  479.     fputs("0x", stdout);
  480.     }
  481.     printf("%08lx%08lx%08lx%08lx%08lx",
  482.     shsInfo->digest[0], shsInfo->digest[1], shsInfo->digest[2],
  483.     shsInfo->digest[3], shsInfo->digest[4]);
  484.     if (dot_zero) {
  485.     fputs(".0", stdout);
  486.     }
  487. }
  488.  
  489.  
  490. /*
  491.  * shsTimeTrial - measure the speed of SHS
  492.  *
  493.  * Measures user time required to digest TEST_MEG megabytes of characters.
  494.  *
  495.  * This function will time blocking and under xor feedback mode if they
  496.  * are set.
  497.  */
  498. static void
  499. shsTimeTrial()
  500. {
  501.     ULONG data[READWORDS];    /* test buffer */
  502.     SHS_INFO shsInfo;        /* hash state */
  503.     struct rusage start;    /* test start time */
  504.     struct rusage stop;        /* test end time */
  505.     double usrsec;        /* duration of test in user seconds */
  506.     unsigned int i;
  507.  
  508.     /*
  509.      * initialize test data
  510.      */
  511.     for (i = 0; i < READSIZE; i++) {
  512.     ((BYTE *)data)[i] = (BYTE)(i & 0xFF);
  513.     }
  514.  
  515.     /*
  516.      * announce test
  517.      */
  518.     if (!q_flag) {
  519.     printf("shs time trial for %d megs of test data ...", TEST_MEG);
  520.     fflush(stdout);
  521.     }
  522.  
  523.     /* 
  524.      * digest data in READSIZE byte chunks
  525.      */
  526.     getrusage(RUSAGE_SELF, &start);
  527.     shsInit(&shsInfo);
  528.     for (i=0; i < TEST_CHUNKS; ++i) {
  529.     shsfullUpdate(&shsInfo, (BYTE *)data, READSIZE);
  530.     }
  531.     COUNT(&shsInfo, READSIZE*TEST_CHUNKS);
  532.     shsFinal(&shsInfo);
  533.     getrusage(RUSAGE_SELF, &stop);
  534.  
  535.     /*
  536.      * announce the test results
  537.      */
  538.     usrsec = (stop.ru_utime.tv_sec - start.ru_utime.tv_sec) +
  539.            (double)(stop.ru_utime.tv_usec - start.ru_utime.tv_usec)/1000000.0;
  540.     if (!q_flag) {
  541.     putchar('\n');
  542.     }
  543.     shsPrint(&shsInfo);
  544.     if (q_flag) {
  545.     putchar('\n');
  546.     } else {
  547.     printf(" is digest of test data\n");
  548.     printf("user seconds to process test data: %.2f\n", usrsec);
  549.     printf("characters processed per user second: %d\n",
  550.         (int)((double)TEST_MEG*1024.0*1024.0/usrsec));
  551.     }
  552. }
  553.  
  554.  
  555. /*
  556.  * shsTestSuite - run a standard suite of test data
  557.  */
  558. static void
  559. shsTestSuite()
  560. {
  561.     struct shs_test *t;        /* current shs test */
  562.     struct stat buf;        /* stat of a test file */
  563.     SHS_INFO digest;        /* test digest */
  564.     char **f;            /* current file being tested */
  565.     int i;
  566.  
  567.     /*
  568.      * copy our test strings into writable data
  569.      */
  570.     for (i=0, t=shs_test_data; i < MAX_SHS_TEST_DATA; ++i, ++t) {
  571.     if (t->ro_data != NULL) {
  572.         t->data = (BYTE *)malloc(t->len + 1);
  573.         if (t->data == NULL) {
  574.         fprintf(stderr, "%s: malloc #4 failed\n", program);
  575.         exit(4);
  576.         }
  577.         strcpy((char *)t->data, (char *)t->ro_data);
  578.         }
  579.     }
  580.  
  581.     /*
  582.      * print test header
  583.      */
  584.     puts("shs test suite results:");
  585.  
  586.     /*
  587.      * find all of the test files
  588.      */
  589.     for (i=0, f=shs_test_file; i < MAX_SHS_TEST_FILE; ++i, ++f) {
  590.     if (stat(*f, &buf) < 0) {
  591.         /* no file1 in this directory, cd to the test suite directory */
  592.         if (chdir(TLIB) < 0) {
  593.         fflush(stdout);
  594.         fprintf(stderr,
  595.             "%s: cannot find %s or %s/%s\n", program, *f, TLIB, *f);
  596.         return;
  597.         }
  598.     }
  599.     }
  600.  
  601.     /*
  602.      * try all combinations of test strings as prefixes and data
  603.      */
  604.     for (i=0, t=shs_test_data; i < MAX_SHS_TEST_DATA; ++i, ++t) {
  605.     shsInit(&digest);
  606.     shsUpdate(&digest, t->data, t->len);
  607.     COUNT(&digest, t->len);
  608.     shsOutput((char *)t->ro_data, 1, &digest);
  609.     }
  610.  
  611.     /*
  612.      * try the files with all test strings as prefixes
  613.      */
  614.     for (i=0, f=shs_test_file; i < MAX_SHS_TEST_FILE; ++i, ++f) {
  615.     shsInit(&digest);
  616.     shsFile(NULL, 0, *f, &digest);
  617.     shsOutput(*f, 0, &digest);
  618.     }
  619.     exit(0);
  620. }
  621.  
  622.  
  623. /*
  624.  * shsPreFileRead - read and process a prepend file
  625.  *
  626.  * Returns the length of pre_str, and modifies pre_str to
  627.  * point at the malloced prepend data.
  628.  */
  629. static int
  630. shsPreFileRead(pre_file, buf)
  631.     char *pre_file;        /* form pre_str from file pre_file */
  632.     BYTE **buf;            /* pointer to pre_str pointer */
  633. {
  634.     struct stat statbuf;    /* stat for pre_file */
  635.     int pre_len;        /* length of pre_file to be used */
  636.     int bytes;            /* bytes read from pre_file */
  637.     FILE *pre;            /* pre_file descriptor */
  638.  
  639.     /* obtain the length that we will use */
  640.     if (stat(pre_file, &statbuf) < 0) {
  641.     fprintf(stderr, "%s: unpable to find prepend file %s\n",
  642.         program, pre_file);
  643.     exit(5);
  644.     }
  645.     pre_len = statbuf.st_size;
  646.     if (pre_len > MAX_PRE_FILE) {
  647.     /* don't use beyond MAX_PRE_FILE in size */
  648.     pre_len = MAX_PRE_FILE;
  649.     }
  650.  
  651.     /* malloc our pre string */
  652.     *buf = (BYTE *)malloc(pre_len+1);
  653.     if (*buf == NULL) {
  654.     fprintf(stderr, "%s: malloc #3 failed\n", program);
  655.     exit(6);
  656.     }
  657.  
  658.     /* open our pre_file */
  659.     pre = fopen(pre_file, "rb");
  660.     if (pre == NULL) {
  661.     fprintf(stderr, "%s: unable to open prepend file %s\n",
  662.       program, pre_file);
  663.     exit(7);
  664.     }
  665.  
  666.     /* read our pre_file data */
  667.     bytes = fread((char *)(*buf), 1, pre_len, pre);
  668.     if (bytes != pre_len) {
  669.     fprintf(stderr,
  670.       "%s: unable to read %d bytes from prepend file %s\n",
  671.       program, pre_len, pre_file);
  672.     exit(8);
  673.     }
  674.  
  675.     /* return our length */
  676.     return (pre_len);
  677. }
  678.  
  679.  
  680. /*
  681.  * shsHelp - print shs help message and exit
  682.  */
  683. static void
  684. shsHelp()
  685. {
  686.     fprintf(stderr,
  687.       "%s [-cd%shiqtx][-p prefix][-P pfile][-s str] [file ...]\n", 
  688. #if defined(DEBUG)
  689.       "D",
  690. #else
  691.       "",
  692. #endif
  693.       program);
  694.     fprintf(stderr,
  695.       "    -c          print C style digests with a leading 0x\n");
  696.     fprintf(stderr,
  697.       "    -d          dual digests of even and odd indexed bytes\n");
  698. #if defined(DEBUG)
  699.     fprintf(stderr,
  700.       "    -D          debug mode\n");
  701. #endif
  702.     fprintf(stderr,
  703.       "    -h          prints this message\n");
  704.     fprintf(stderr,
  705.       "    -i          process inode and filename as well as file data\n");
  706.     fprintf(stderr,
  707.       "    -p prefix   prepend str to data before digesting\n");
  708.     fprintf(stderr,
  709.       "    -P pfile    prepend the file 'str' to data before digesting\n");
  710.     fprintf(stderr,
  711.       "    -q          print only the digest\n");
  712.     fprintf(stderr,
  713.       "    -r          reverse feedback mode\n");
  714.     fprintf(stderr,
  715.       "    -s str      prints digest and contents of string\n");
  716.     fprintf(stderr,
  717.       "    -t          prints time statistics for %dM chars\n", TEST_MEG);
  718.     fprintf(stderr,
  719.       "    -v          print version\n");
  720.     fprintf(stderr,
  721.       "    -x          execute an extended standard suite of test data\n");
  722.     fprintf(stderr,
  723.       "    file        print digest and name of file\n");
  724.     fprintf(stderr,
  725.       "    (no args)   print digest of stdin\n");
  726.     exit(0);
  727. }
  728.  
  729.  
  730. /*
  731.  * main - shs main control function
  732.  */
  733. void
  734. main(argc, argv)
  735.     int argc;            /* arg count */
  736.     char **argv;        /* the args */
  737. {
  738.     SHS_INFO digest;        /* our current digest */
  739.     BYTE *pre_str = NULL;    /* pre-process this data first */
  740.     char *pre_file = NULL;    /* pre-process this file first */
  741.     char *data_str = NULL;    /* data is this string, not a file */
  742.     UINT pre_str_len;        /* length of pre_str or pre_file */
  743.     UINT data_str_len;        /* length of data_str */
  744.     int d_flag = 0;        /* 1 => dual digest mode */
  745.     int t_flag = 0;        /* 1 => -t was given */
  746.     int x_flag = 0;        /* 1 => -x was given */
  747.     extern char *optarg;    /* argument to option */
  748.     extern int optind;        /* option index */
  749.     int c;
  750.  
  751.     /*
  752.      * parse args
  753.      */
  754.     program = argv[0];
  755.     while ((c = getopt(argc, argv, "cdDihp:P:qs:tvx")) != -1) {
  756.         switch (c) {
  757.         case 'c':
  758.         c_flag = 1;
  759.         break;
  760.         case 'd':
  761.         d_flag = 1;
  762.         break;
  763.         case 'D':
  764. #if defined(DEBUG)
  765.         debug = 1;
  766. #else
  767.         fprintf(stderr, "%s: not compiled with -DDEBUG\n", program);
  768.         exit(9);
  769.         /*NOTREACHED*/
  770. #endif
  771.         break;
  772.     case 'h':
  773.         shsHelp();
  774.         /*NOTREACHED*/
  775.         break;
  776.     case 'i':
  777.             i_flag = 1;
  778.             break;
  779.     case 'p':
  780.         pre_str = (BYTE *)optarg;
  781.         break;
  782.     case 'q':
  783.         q_flag = 1;
  784.         break;
  785.     case 'P':
  786.         pre_file = optarg;
  787.         break;
  788.         case 's':
  789.             data_str = optarg;
  790.             break;
  791.     case 't':
  792.         t_flag = 1;
  793.         break;
  794.     case 'v':
  795.         printf("%s: version 2.%s.%s%s %s\n",
  796.             program, "%R%", "%L%",
  797.             (strcmp(shs_what,"@(#)") == 0 &&
  798.              strcmp("%Z%","@(#)") == 0 &&
  799.              strcmp(shsdual_what,"@(#)") == 0 &&
  800.              strcmp(SHS_H_WHAT,"@(#)") == 0) ? "" : "+",
  801.             "%D%");
  802.         exit(0);
  803.     case 'x':
  804.         x_flag = 1;
  805.         break;
  806.     default:
  807.         shsHelp();
  808.         break;
  809.         }
  810.     }
  811.     /* arg checking */
  812.     if (data_str && optind != argc) {
  813.     fprintf(stderr, "%s: -s is not compatible with digesting files\n",
  814.         program);
  815.     exit(10);
  816.     }
  817.     if (i_flag && optind == argc) {
  818.     fprintf(stderr, "%s: -i works only on filenames\n", program);
  819.     exit(11);
  820.     }
  821.  
  822.     /*
  823.      * process -x if needed
  824.      */
  825.     if (x_flag) {
  826.         if (d_flag) {
  827.             dualTest();
  828.     } else {
  829.         shsTestSuite();
  830.     }
  831.     exit(0);
  832.     }
  833.  
  834.     /*
  835.      * process -t if needed
  836.      */
  837.     if (t_flag) {
  838.     shsTimeTrial();
  839.     exit(0);
  840.     }
  841.  
  842.     /*
  843.      * process -P or -p if needed
  844.      */
  845.     if (pre_str && pre_file) {
  846.     fprintf(stderr, "%s: -p and -P conflict\n", program);
  847.     exit(12);
  848.     }
  849.     if (pre_file) {
  850.     pre_str_len = shsPreFileRead(pre_file, &pre_str);
  851.     } else if (pre_str) {
  852.         pre_str_len = strlen((char *)pre_str);
  853.     } else {
  854.         pre_str_len = 0;
  855.     }
  856.     if (pre_str_len > MAX_PRE_FILE) {
  857.     fprintf(stderr, "%s: prefix may not be longer than %d bytes\n",
  858.         program, MAX_PRE_FILE);
  859.         exit(13);
  860.     }
  861.  
  862.     /*
  863.      * if -d, perform dual digest processing instead
  864.      */
  865.     if (d_flag) {
  866.     dualMain(argc, argv, pre_str, pre_str_len, data_str);
  867.  
  868.     /*
  869.      * if no -d, process string, stdin or files
  870.      */
  871.     } else {
  872.  
  873.     /*
  874.      * case: digest a string
  875.      */
  876.     if (data_str != NULL) {
  877.         data_str_len = strlen(data_str);
  878.         shsInit(&digest);
  879.         shsUpdate(&digest, pre_str, pre_str_len);
  880.         shsUpdate(&digest, (BYTE *)data_str, data_str_len);
  881.         COUNT(&digest, pre_str_len + data_str_len);
  882.         shsOutput(data_str, 1, &digest);
  883.  
  884.     /*
  885.      * case: digest stdin
  886.      */
  887.     } else if (optind == argc) {
  888.         shsInit(&digest);
  889.         shsStream(pre_str, pre_str_len, stdin, &digest);
  890.         shsOutput(NULL, 0, &digest);
  891.  
  892.     /*
  893.      * case: digest files
  894.      */
  895.     } else {
  896.         if (i_flag) {
  897.         dot_zero = 1;
  898.         }
  899.         for (; optind < argc; optind++) {
  900.         shsInit(&digest);
  901.         shsFile(pre_str, pre_str_len, argv[optind], &digest);
  902.         shsOutput(argv[optind], 0, &digest);
  903.         }
  904.     }
  905.     }
  906.  
  907.     /* all done */
  908.     exit(0);
  909. }
  910.